<template>
  <!-- form表单组件 -->
  <el-form
    v-if="model"
    ref="formRef"
    class="def-form"
    :model="model"
    statusIcon
    labelSuffix=":"
    :validate-on-rule-change="false"
    v-bind="$attrs">
    <slot name="header"></slot>
    <!-- 响应式栅格布局 -->
    <el-row class="def-form-row" :gutter="layout.gutter">
      <template v-for="item in formOptions" :key="item.prop">
        <!-- 其他表单项处理 -->
        <el-col v-if="!item.hidden && item.element !== 'button'" v-bind="item.layout || layout">
          <el-form-item
            :label="item.label"
            :prop="item.prop"
            :labelWidth="item.labelWidth"
            :required="item.required"
            :rules="item.rules"
            :error="item.error"
            :showMessage="item.showMessage"
            :inlineMessage="item.inlineMessage"
            :class="item.class"
            :style="item.style">
            <!-- 级联选择器单独处理 -->
            <template v-if="item.element === 'cascader'">
              <component
                :is="'el-' + item.element"
                v-model="model[item.prop]"
                :options="item.children"
                v-bind="item?.attrs?.cascader"></component>
            </template>
            <!-- 图标选择器 -->
            <template v-else-if="item.element === 'def-icon-picker'">
              <component
                ref="iconPickerRef"
                :is="item.element"
                v-model:iconName="model[item.prop]"
                v-bind="item?.attrs?.iconPicker"></component>
            </template>
            <!-- 行政区选择器 -->
            <template v-else-if="item.element === 'def-region-picker'">
              <component
                ref="regionPickerRef"
                :is="item.element"
                v-model:region="model[item.prop]"
                v-bind="item?.attrs?.regionPicker"></component>
            </template>
            <!-- wangEditor 富文本编辑器 -->
            <template v-else-if="item.element === 'def-editor'">
              <component
                ref="editorRef"
                :is="item.element"
                v-model:content="model[item.prop]"
                v-bind="item?.attrs?.editor"></component>
            </template>
            <!-- 上传组件 -->
            <template v-else-if="item.element === 'def-upload'">
              <component
                ref="uploadRef"
                :is="item.element"
                v-model:fileList="model[item.prop]"
                v-bind="item?.attrs?.upload"></component>
            </template>
            <!-- 其他表单项 -->
            <template v-else>
              <component
                :is="'el-' + item.element"
                v-model="model[item.prop]"
                v-bind="attrsBind(item)">
                <template v-if="item.children && item.children.length">
                  <template v-for="subItem in item.children" :key="subItem.value">
                    <!-- 单选框 radio / 多选框 checkbox 特殊处理 -->
                    <template v-if="item.subElement === 'radio' || item.subElement === 'checkbox'">
                      <component :is="'el-' + item.subElement" :label="subItem.value">
                        <slot v-if="subItem.label">{{ subItem.label }}</slot>
                      </component>
                    </template>
                    <template v-else-if="item.subElement === 'option'">
                      <component
                        :is="'el-' + item.subElement"
                        :label="subItem.label"
                        :value="subItem.value"></component>
                    </template>
                  </template>
                </template>
              </component>
            </template>
          </el-form-item>
        </el-col>
        <!--  按钮表单项处理  -->
        <el-col
          v-if="!item.hidden && item.element === 'button' && !hiddenBtn"
          class="def-form-col-action"
          v-bind="item.layout || layout">
          <el-form-item
            :label="item.label"
            :prop="item.prop"
            :labelWidth="item.labelWidth"
            :required="item.required"
            :rules="item.rules"
            :error="item.error"
            :showMessage="item.showMessage"
            :inlineMessage="item.inlineMessage"
            :class="item.class"
            :style="{ justifyContent: 'flex-end', ...item.style }">
            <!-- 自定义按钮 -->
            <template v-if="item.action?.length">
              <template v-for="subItem in item.action" :key="subItem.event">
                <component
                  v-if="!subItem.hidden"
                  :is="'el-' + item.element"
                  :type="subItem.type"
                  :size="subItem.size"
                  :disabled="subItem.disabled || isLoading"
                  v-bind="{ ...item.actionAttrs, ...subItem, icon: '' }"
                  @click="handleEvent(subItem.event)">
                  <def-icon
                    v-if="subItem.icon"
                    :icon="subItem.icon"
                    :style="{ fontSize: subItem.iconSize + 'px' }" />
                  <span class="ml-05">{{ subItem.name }}</span>
                </component>
              </template>
            </template>
            <!-- 默认按钮 -->
            <template v-else>
              <!-- 查询 -->
              <template v-if="item.actionType === 'query'">
                <def-button
                  v-if="!item.hiddenSearch"
                  type="primary"
                  :disabled="isLoading"
                  v-bind="item.actionAttrs"
                  @click="handleEvent('search')">
                  <def-icon icon="Search" />
                  <span class="ml-05">查询</span>
                </def-button>
                <def-button
                  v-if="!item.hiddenReset"
                  :disabled="isLoading"
                  v-bind="item.actionAttrs"
                  @click="handleEvent('reset')">
                  <def-icon icon="RefreshRight" />
                  <span class="ml-05">重置</span>
                </def-button>
                <!--                 <def-button
                  v-if="!item.hiddenClear"
                  type="danger"
                  v-bind="item.actionAttrs"
                  @click="handleEvent('clear')">
                  <def-icon icon="CircleClose" />
                  <span class="ml-05">清空</span>
                </def-button> -->
              </template>
              <!-- 表单 -->
              <template v-if="item.actionType === 'form'">
                <def-button
                  v-if="!item.hiddenConfirm"
                  type="primary"
                  :disabled="isLoading"
                  v-bind="item.actionAttrs"
                  @click="handleEvent('confirm')">
                  <def-icon icon="CircleCheck" />
                  <span class="ml-05">确认</span>
                </def-button>
                <def-button
                  v-if="!item.hiddenCancel"
                  type="default"
                  :disabled="isLoading"
                  v-bind="item.actionAttrs"
                  @click="handleEvent('cancel')">
                  <def-icon icon="CircleClose" />
                  <span class="ml-05">取消</span>
                </def-button>
              </template>
            </template>
          </el-form-item>
        </el-col>
      </template>
    </el-row>
    <slot name="footer"></slot>
  </el-form>
</template>

<script lang="ts" setup>
import { computed, inject, PropType, ref, watchEffect } from 'vue'
import { IFormItem, ILayout, TActionEvent } from './types'
import { ElMessage, FormInstance, FormRules } from 'element-plus/es'

/* 参数声明 */
const props = defineProps({
  // 表单项配置数组
  formOptions: {
    type: Array as PropType<IFormItem[]>,
    required: true
  },
  // 表单回显数据
  formData: {
    type: Object,
    default: () => null
  },
  // 表单整体布局
  layout: {
    type: Object as PropType<ILayout>,
    default: () => ({})
  },
  // dialog 隐藏 form 按钮
  hiddenBtn: {
    type: Boolean,
    default: false
  }
})
/* 事件声明 */
const emits = defineEmits(['confirm', 'cancel', 'search', 'reset'])
const isLoading = inject('isLoading')

/* 表单处理 */
const formRef = ref<FormInstance>()
const model = ref<Record<string, any>>({})
const rules = ref<FormRules>({})
// 其他表单项属性绑定处理
const kebabCaseToCamelCase = (value: string) => {
  if (!value.includes('-')) return value
  const arr = value.split('-')
  return (
    arr[0] +
    arr
      .slice(1)
      .map((str: string) => str[0].toUpperCase() + str.slice(1))
      .join('')
  )
}
const attrsBind = computed(() => {
  return (item: any) => {
    if (item.attrs) return item.attrs[kebabCaseToCamelCase(item.element)]
  }
})
// 表单校验
const verifyForm = () => {
  if (!props.formOptions?.length) throw new Error('缺少表单项配置')
  const len = props.formOptions.length
  const actionIndex = props.formOptions.findIndex((item) => item.element === 'button')
  if (actionIndex !== -1 && actionIndex !== len - 1) {
    throw new Error('def-button 操作项必须配置在表单项最后')
  }
}
// 初始化表单
const initForm = () => {
  verifyForm()
  model.value = {} // 清空表格编辑时留下的数据
  props.formOptions.forEach((item) => {
    if (item.element === 'button') return
    model.value[item.prop as string] = item.value
    if (item.rules) rules.value[item.prop as string] = item.rules
  })
}

watchEffect(() => {
  // 表格编辑把表格数据赋值表单项
  if (props.formData) {
    model.value = props.formData
    return
  }
  // 表格新增重置表单项数据
  initForm()
})

// 确认/取消/查询/重置 action 事件处理
const actionEvent = {
  CONFIRM: 'confirm',
  CANCEL: 'cancel',
  SEARCH: 'search',
  RESET: 'reset'
}
// 注入查询重置页码为 1
const resetCurrentPage: any = inject('resetCurrentPage')
const handleConfirm = async () => {
  try {
    await formRef.value?.validate()
    emits('confirm', model.value)
    handleClear()
    ElMessage.success('操作成功')
  } catch {
    ElMessage.error('表单输入不完整')
  }
}
const handleCancel = () => {
  emits('cancel')
}

defineExpose({
  exposeConfirm: handleConfirm,
  exposeCancel: handleCancel
})
const handleEvent = async (event: TActionEvent) => {
  switch (event) {
    case actionEvent.CONFIRM:
      console.log('确认')
      await handleConfirm()
      break
    case actionEvent.CANCEL:
      console.log('取消')
      handleCancel()
      break
    case actionEvent.SEARCH:
      console.log('查询')
      resetCurrentPage()
      emits('search', model.value)
      break
    case actionEvent.RESET:
      console.log('重置')
      // initForm()
      handleClear()
      emits('reset')
      break
    default:
      throw new Error('表单按钮 action 事件处理错误')
  }
}
// 获取组件实例
const editorRef = ref<any>()
const iconPickerRef = ref<any>()
const regionPickerRef = ref<any>()
const uploadRef = ref<any>()
// 清空表单组件内容
const handleClear = () => {
  formRef.value?.clearValidate()
  formRef.value?.resetFields()
  'action' in model.value && delete model.value.action
  iconPickerRef.value?.[0]?.clear()
  regionPickerRef.value?.[0]?.clear()
  editorRef.value?.[0]?.clear()
  uploadRef.value?.[0]?.clear()
}
</script>

<style scoped lang="scss">
.def-form {
  @include clearElBtnFocus;
  min-width: 400px;
  .el-col {
    margin: 5px 0;
  }
  :deep(.el-form-item) {
    .el-form-item__label {
      font-weight: 700;
    }
    .el-form-item__content {
      // 继承el-form-item样式来调整按钮位置
      justify-content: inherit;
      .el-textarea {
        height: 100%;
      }
      .el-textarea__inner {
        height: 100%;
      }
      .el-rate__item {
        line-height: normal;
      }
    }
  }
  .ml-05 {
    margin-left: 5px;
  }
}
</style>
